﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;

namespace Tomahawk.Runtime.Rendering.Materials
{
    #region SAS Technique Exceptions
    public class SASUninitializedTechniqueException : Exception
    {
    }
    #endregion

    public sealed class SASTechnique
    {
        private SASScript script;
        public SASScript Script { get { return script; } }

        private bool valid = false;
        public bool Valid { get { return valid; } }

        private SASEffect sasEffect;
        public SASEffect Effect { get { return sasEffect; } }

        private EffectTechnique technique;
        public EffectTechnique Technique { get { return technique; } }

        private Dictionary<string, SASPass> passTable = new Dictionary<string, SASPass>(StringComparer.OrdinalIgnoreCase);
        public Dictionary<string, SASPass> PassTable { get { return passTable; } }

        public string Name { 
            get 
            {
                if (technique == null) throw new SASUninitializedTechniqueException();
                return technique.Name; 
            } 
        }

        public bool Initialize(SASEffect sasEffect, EffectTechnique technique)
        {
            this.sasEffect = sasEffect;
            this.technique = technique;
            this.valid = technique.Validate();

            foreach (EffectAnnotation annotation in technique.Annotations)
            {
                if (string.Compare(annotation.Name, "SCRIPT", true) == 0)
                {
                    this.script = new SASScript(annotation.GetValueString());
                }
            }

            foreach (EffectPass pass in technique.Passes)
            {
                SASPass sasPass = new SASPass();
                sasPass.Initialize(this, pass);
                passTable.Add(sasPass.Name, sasPass);
            }

            return true;
        }

        #region Rendering
        public bool DrawMesh(ref SASScript.Context context)
        {
            bool result = true;

            if (script != null)
            {
                result = script.Execute(ref context);
            }
            else
            {
                int passCount = technique.Passes.Count;
                for (int passIdx = 0; passIdx < passCount; ++passIdx)
                {
                    EffectPass pass = technique.Passes[passIdx];
                    SASPass sasPass = null;
                    if (passTable.TryGetValue(pass.Name, out sasPass))
                    {
                        context.sasPass = sasPass;
                        result &= context.sasPass.DrawMesh(ref context);
                    }
                }
            }

            return result;
        }
        public bool DrawScene(GraphicsDevice device)
        {
            bool result = true;

            SASScript.Context context = SASScript.Context.EmptyContext;
            context.sasEnvironment = Engine.Instance.RenderManager.Environment;
            context.sasEffect = sasEffect;
            context.sasTechnique = this;
            context.device = device;

            if (script != null)
            {
                result = script.Execute(ref context);
            }
            else
            {
                context.device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
                SASEffect.currentRenderGroup = SASEffect.RenderGroup.Color;

                int passCount = technique.Passes.Count;
                for (int passIdx = 0; passIdx < passCount; ++passIdx)
                {
                    EffectPass pass = technique.Passes[passIdx];
                    SASPass sasPass = null;
                    if (passTable.TryGetValue(pass.Name, out sasPass))
                    {
                        context.sasPass = sasPass;
                        if (context.sasPass.Script != null)
                        {
                            result = context.sasPass.DrawMesh(ref context);
                        }
                        else
                        {
                            result = context.sasEffect.RaiseDrawGeometry("COLOR", SASEffect.currentRenderGroup, context.device);
                        }
                    }
                }

                Engine.Instance.CanvasManager.Render();
            }

            return result;
        }
        #endregion
    }
}
